<?php

final class Buffer
{
	/**
	 * constants
	 */
	const END_CLEAN = 0;
	const GET_CLEAN = 1;
	const TRACE_DEPTH = 1;
	
	/**
	 * static properties
	 */
	private static $buffers = array();
	private static $to_end = -1;
	
	/**
	 * instance properties
	 */
	private $start_time;
	private $start_file;
	private $start_line;
	private $end_time;
	private $end_file;
	private $end_line;
	
	/**
	 * static methods
	 */
	public static function start()
	{
		self::$buffers[++self::$to_end] = new Buffer();
	}
	public static function endClean()
	{
		self::end(self::END_CLEAN);
	}
	public static function getClean()
	{
		return self::end(self::GET_CLEAN);
	}
	private static function end($type)
	{
		if (self::$to_end < 0)
		{
			$backtrace = debug_backtrace();
			$depth = 1;
			throw new Buffer_UnendedBufferException(sprintf("Cannot end another buffer: no buffer to end (invoked in %s (%s))", $backtrace[$depth]["file"], $backtrace[$depth]["line"]));
		}
		else
		{
			switch ($type)
			{
				case self::END_CLEAN:
					self::$buffers[self::$to_end--] -> _endClean();
					
					break;
				case self::GET_CLEAN:
					return self::$buffers[self::$to_end--] -> _getClean();
					
					break;
			}
		}
	}
	public static function getBuffers()
	{
		return self::$buffers;
	}
	
	/**
	 * instance methods
	 */
	private function __construct()
	{
		$backtrace = debug_backtrace();
		
		$this -> start_time = microtime(true);
		$this -> start_file = $backtrace[self::TRACE_DEPTH]["file"];
		$this -> start_line = $backtrace[self::TRACE_DEPTH]["line"];
		
		ob_start();
	}
	private function __clone()
	{
	}
	
	public function _endClean()
	{
		ob_end_clean();
		
		$backtrace = debug_backtrace();
		
		$this -> end_time = microtime(true);
		$this -> end_file = $backtrace[self::TRACE_DEPTH]["file"];
		$this -> end_line = $backtrace[self::TRACE_DEPTH]["line"];
	}
	public function _getClean()
	{
		$output = ob_get_clean();
		
		$backtrace = debug_backtrace();
		
		$this -> end_time = microtime(true);
		$this -> end_file = $backtrace[self::TRACE_DEPTH]["file"];
		$this -> end_line = $backtrace[self::TRACE_DEPTH]["line"];
		
		return $output;
	}
	public function getStartFile()
	{
		return $this -> start_file;
	}
	public function getStartLine()
	{
		return $this -> start_line;
	}
	public function getEndFile()
	{
		return $this -> end_file;
	}
	public function getEndLine()
	{
		return $this -> end_line;
	}
}